package com.coai.rtc.service

import com.coai.common.exception.ResourceNotFoundException
import com.coai.common.exception.ValidationException
import com.coai.rtc.dto.*
import com.coai.rtc.model.*
import com.coai.rtc.repository.CallParticipantRepository
import com.coai.rtc.repository.CallRepository
import org.springframework.data.domain.PageRequest
import org.springframework.messaging.simp.SimpMessagingTemplate
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit

@Service
class CallService(
    private val callRepository: CallRepository,
    private val participantRepository: CallParticipantRepository,
    private val messagingTemplate: SimpMessagingTemplate,
    private val rtcEventService: RtcEventService
) {
    
    /**
     * 发起通话
     */
    @Transactional
    fun initiateCall(request: InitiateCallRequest, callerId: String): CallDto {
        // 创建通话记录
        val call = Call(
            callType = request.callType,
            mediaType = request.mediaType,
            callerId = callerId,
            conversationId = request.conversationId,
            status = CallStatus.INITIATED
        )
        
        val savedCall = callRepository.save(call)
        
        // 添加发起人作为参与者
        participantRepository.save(
            CallParticipant(
                callId = savedCall.id!!,
                userId = callerId,
                status = ParticipantStatus.JOINED,
                joinedAt = LocalDateTime.now(),
                videoEnabled = request.mediaType == MediaType.VIDEO
            )
        )
        
        // 添加其他参与者（如果有）
        request.participantIds?.forEach { userId ->
            participantRepository.save(
                CallParticipant(
                    callId = savedCall.id,
                    userId = userId,
                    status = ParticipantStatus.INVITED,
                    videoEnabled = request.mediaType == MediaType.VIDEO
                )
            )
            
            // 推送邀请通知
            sendCallInvite(savedCall.id, callerId, userId)
        }
        
        // 更新通话状态为响铃
        savedCall.status = CallStatus.RINGING
        callRepository.save(savedCall)
        
        // 发送业务事件
        rtcEventService.publishCallInitiated(savedCall)
        
        return toDto(savedCall)
    }
    
    /**
     * 接受通话
     */
    @Transactional
    fun acceptCall(callId: String, userId: String): CallDto {
        val call = getCallOrThrow(callId)
        val participant = participantRepository.findByCallIdAndUserId(callId, userId)
            ?: throw ValidationException("您不在此通话中")
        
        // 更新参与者状态
        participant.status = ParticipantStatus.JOINED
        participant.joinedAt = LocalDateTime.now()
        participantRepository.save(participant)
        
        // 如果是第一个接听的参与者，更新通话状态
        if (call.status == CallStatus.RINGING) {
            call.status = CallStatus.CONNECTED
            call.startTime = LocalDateTime.now()
            callRepository.save(call)
        }
        
        // 通知其他参与者
        broadcastToCall(callId, SignalingMessage(
            type = SignalingType.PARTICIPANT_JOIN,
            callId = callId,
            from = userId,
            data = mapOf(
                "userId" to userId,
                "userName" to getUserName(userId),
                "joinedAt" to LocalDateTime.now().toString()
            )
        ))
        
        rtcEventService.publishParticipantJoined(call, participant)
        
        return toDto(call)
    }
    
    /**
     * 拒绝通话
     */
    @Transactional
    fun rejectCall(callId: String, userId: String): CallDto {
        val call = getCallOrThrow(callId)
        val participant = participantRepository.findByCallIdAndUserId(callId, userId)
            ?: throw ValidationException("您不在此通话中")
        
        participant.status = ParticipantStatus.REJECTED
        participantRepository.save(participant)
        
        // 通知发起人
        sendToUser(call.callerId, SignalingMessage(
            type = SignalingType.CALL_REJECT,
            callId = callId,
            from = userId,
            data = mapOf("userId" to userId)
        ))
        
        // 如果所有被邀请者都拒绝，结束通话
        val activeParticipants = participantRepository.findActiveParticipants(callId)
        if (activeParticipants.isEmpty() || activeParticipants.all { it.userId == call.callerId }) {
            call.status = CallStatus.REJECTED
            call.endTime = LocalDateTime.now()
            callRepository.save(call)
        }
        
        return toDto(call)
    }
    
    /**
     * 结束通话
     */
    @Transactional
    fun endCall(callId: String, userId: String): CallDto {
        val call = getCallOrThrow(callId)
        
        // 只有发起人或参与者可以结束通话
        val participant = participantRepository.findByCallIdAndUserId(callId, userId)
        if (participant == null && call.callerId != userId) {
            throw ValidationException("您无权结束此通话")
        }
        
        // 更新通话状态
        call.status = CallStatus.ENDED
        call.endTime = LocalDateTime.now()
        
        // 计算通话时长
        if (call.startTime != null) {
            call.duration = ChronoUnit.SECONDS.between(call.startTime, call.endTime)
        }
        
        callRepository.save(call)
        
        // 更新所有参与者状态
        val participants = participantRepository.findByCallId(callId)
        participants.forEach { p ->
            if (p.status == ParticipantStatus.JOINED) {
                p.status = ParticipantStatus.LEFT
                p.leftAt = LocalDateTime.now()
                participantRepository.save(p)
            }
        }
        
        // 通知所有参与者
        broadcastToCall(callId, SignalingMessage(
            type = SignalingType.CALL_END,
            callId = callId,
            from = userId,
            data = mapOf(
                "endedBy" to userId,
                "duration" to (call.duration ?: 0)
            )
        ))
        
        rtcEventService.publishCallEnded(call)
        
        return toDto(call)
    }
    
    /**
     * 离开通话（参与者离开，但通话继续）
     */
    @Transactional
    fun leaveCall(callId: String, userId: String): CallDto {
        val call = getCallOrThrow(callId)
        val participant = participantRepository.findByCallIdAndUserId(callId, userId)
            ?: throw ValidationException("您不在此通话中")
        
        participant.status = ParticipantStatus.LEFT
        participant.leftAt = LocalDateTime.now()
        participantRepository.save(participant)
        
        // 通知其他参与者
        broadcastToCall(callId, SignalingMessage(
            type = SignalingType.PARTICIPANT_LEAVE,
            callId = callId,
            from = userId,
            data = mapOf("userId" to userId)
        ))
        
        // 如果没有活跃参与者了，结束通话
        val activeParticipants = participantRepository.findActiveParticipants(callId)
        if (activeParticipants.isEmpty()) {
            call.status = CallStatus.ENDED
            call.endTime = LocalDateTime.now()
            if (call.startTime != null) {
                call.duration = ChronoUnit.SECONDS.between(call.startTime, call.endTime)
            }
            callRepository.save(call)
        }
        
        return toDto(call)
    }
    
    /**
     * 更新媒体状态
     */
    @Transactional
    fun updateMediaState(
        callId: String,
        userId: String,
        request: UpdateMediaStateRequest
    ): ParticipantDto {
        val participant = participantRepository.findByCallIdAndUserId(callId, userId)
            ?: throw ValidationException("您不在此通话中")
        
        request.muted?.let { participant.muted = it }
        request.videoEnabled?.let { participant.videoEnabled = it }
        request.screenSharing?.let { participant.screenSharing = it }
        
        val updated = participantRepository.save(participant)
        
        // 通知其他参与者
        broadcastToCall(callId, SignalingMessage(
            type = SignalingType.MEDIA_STATE_CHANGE,
            callId = callId,
            from = userId,
            data = mapOf(
                "userId" to userId,
                "muted" to updated.muted,
                "videoEnabled" to updated.videoEnabled,
                "screenSharing" to updated.screenSharing
            )
        ))
        
        return toParticipantDto(updated)
    }
    
    /**
     * 获取通话详情
     */
    fun getCall(callId: String): CallDto {
        val call = getCallOrThrow(callId)
        return toDto(call)
    }
    
    /**
     * 获取用户的通话历史
     */
    fun getUserCallHistory(userId: String, page: Int, size: Int): CallListResponse {
        val pageable = PageRequest.of(page, size)
        val calls = callRepository.findUserCalls(userId, pageable)
        
        // TODO: 获取总数
        val total = calls.size.toLong()
        
        return CallListResponse(
            calls = calls.map { toDto(it) },
            hasMore = calls.size >= size,
            total = total
        )
    }
    
    /**
     * 获取通话统计
     */
    fun getCallStatistics(userId: String, days: Int = 30): CallStatistics {
        val startTime = LocalDateTime.now().minusDays(days.toLong())
        val endTime = LocalDateTime.now()
        
        val calls = callRepository.findByCreatedAtBetween(
            startTime, endTime, PageRequest.of(0, 10000)
        ).filter { call ->
            call.callerId == userId || 
            participantRepository.findByCallIdAndUserId(call.id!!, userId) != null
        }
        
        val totalDuration = calls.mapNotNull { it.duration }.sum()
        val audioCallCount = calls.count { it.mediaType == MediaType.AUDIO }.toLong()
        val videoCallCount = calls.count { it.mediaType == MediaType.VIDEO }.toLong()
        val missedCallCount = calls.count { it.status == CallStatus.MISSED }.toLong()
        
        // 按天统计
        val callsByDay = calls.groupBy { 
            it.createdAt.toLocalDate().toString() 
        }.mapValues { it.value.size.toLong() }
        
        return CallStatistics(
            totalCalls = calls.size.toLong(),
            totalDuration = totalDuration,
            averageDuration = if (calls.isNotEmpty()) totalDuration.toDouble() / calls.size else 0.0,
            audioCallCount = audioCallCount,
            videoCallCount = videoCallCount,
            missedCallCount = missedCallCount,
            callsByDay = callsByDay
        )
    }
    
    // 辅助方法
    
    private fun getCallOrThrow(callId: String): Call {
        return callRepository.findById(callId)
            .orElseThrow { ResourceNotFoundException("通话不存在") }
    }
    
    private fun sendCallInvite(callId: String, from: String, to: String) {
        sendToUser(to, SignalingMessage(
            type = SignalingType.CALL_INVITE,
            callId = callId,
            from = from,
            to = to,
            data = mapOf(
                "callerId" to from,
                "callerName" to getUserName(from)
            )
        ))
    }
    
    private fun sendToUser(userId: String, message: SignalingMessage) {
        messagingTemplate.convertAndSendToUser(
            userId,
            "/queue/rtc-signal",
            message
        )
    }
    
    private fun broadcastToCall(callId: String, message: SignalingMessage) {
        messagingTemplate.convertAndSend("/topic/call/$callId", message)
    }
    
    private fun toDto(call: Call): CallDto {
        val participants = participantRepository.findByCallId(call.id!!)
        
        return CallDto(
            id = call.id,
            callType = call.callType,
            mediaType = call.mediaType,
            callerId = call.callerId,
            callerName = getUserName(call.callerId),
            conversationId = call.conversationId,
            status = call.status,
            participants = participants.map { toParticipantDto(it) },
            startTime = call.startTime,
            endTime = call.endTime,
            duration = call.duration,
            recordingUrl = call.recordingUrl,
            transcriptUrl = call.transcriptUrl,
            analysisUrl = call.analysisUrl,
            createdAt = call.createdAt
        )
    }
    
    private fun toParticipantDto(participant: CallParticipant): ParticipantDto {
        return ParticipantDto(
            id = participant.id!!,
            userId = participant.userId,
            userName = getUserName(participant.userId),
            userAvatar = null, // TODO: 从用户服务获取
            status = participant.status,
            joinedAt = participant.joinedAt,
            leftAt = participant.leftAt,
            muted = participant.muted,
            videoEnabled = participant.videoEnabled,
            screenSharing = participant.screenSharing
        )
    }
    
    private fun getUserName(userId: String): String {
        // TODO: 从用户服务获取
        return "User_$userId"
    }
}
